#include <QDateTime>
#include <QSslConfiguration>

#include "creplicator.h"

CReplicator::CReplicator(CDatabase *db):
        m_db(db), m_reply(0)
{
    m_manager = new QNetworkAccessManager(this);
/*    connect(m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
            this, SLOT(sslerr(QNetworkReply*,QList<QSslError>)));*/
    connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(readData(QNetworkReply*)));
    //login("login_string", "password_string");
}

void CReplicator::addField(QString name, QString value)
{
    m_fieldNames.append(name);
    m_fieldValues.append(value);
}

void CReplicator::addFile(QString fieldName, QByteArray file, QString name)
{
    m_files.append(file);
    m_fileFieldNames.append(fieldName);
    m_fileNames.append(name);
}

QNetworkReply* CReplicator::post(const QString url)
{
    QString host = url.right(url.length()-url.indexOf("://")-3);
    host = host.left(host.indexOf("/"));

    QString crlf="\r\n";

    qsrand(QDateTime::currentDateTime().toTime_t());

    QString boundary = "---------------------------" +
                       QString::number(qrand()) +
                       QString::number(qrand()) +
                       QString::number(qrand());

    QString endBoundary = crlf + "--" + boundary + "--" + crlf;

    QString contentType = "multipart/form-data; boundary=" + boundary;
    boundary = "--" + boundary + crlf;

    QByteArray bond = boundary.toAscii();
    QByteArray send;
    bool first=true;

    for(int i=0; i<m_fieldNames.size(); i++)
    {
        send.append(bond);

        if (first)
        {
            boundary=crlf+boundary;
            bond=boundary.toAscii();
            first=false;
        }

        send.append(QString("Content-Disposition: form-data; name=\"" + m_fieldNames.at(i) + "\"" + crlf).toAscii());

        send.append(QString("Content-Transfer-Encoding: 8bit" + crlf).toAscii());

        send.append(crlf.toAscii());
        send.append(m_fieldValues.at(i).toUtf8());
    }

    for(int i=0; i<m_files.size(); i++)
    {
        send.append(bond);
        send.append(QString("Content-Disposition: form-data; name=\"" +
                            m_fileFieldNames.at(i) + "\"; filename=\"" + m_fileNames.at(i) +
                            "\"" + crlf).toAscii());
        send.append(QString("Content-Type: application/octet-stream" + crlf + crlf).toAscii());
        send.append(m_files.at(i));
    }

    send.append(endBoundary.toAscii());

    m_fieldNames.clear();
    m_fieldValues.clear();
    m_fileFieldNames.clear();
    m_fileNames.clear();
    m_files.clear();

    QNetworkRequest request;
    request.setRawHeader("Host", host.toAscii());

    request.setRawHeader("User-Agent", QString("Passworder replicator").toAscii());

    request.setHeader(QNetworkRequest::ContentTypeHeader, contentType.toAscii());
    request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(send.size()).toString());
    request.setUrl(QUrl(url));

    //QSslConfiguration ssl_conf;
    //ssl_conf.setPeerVerifyMode(QSslSocket::VerifyNone);

    //request.setSslConfiguration(ssl_conf);

    debug("Send data: " + QString::number(send.size()));
    debug("====================================\n" +
          QString(send) +
          "\n====================================");

    m_reply = m_manager->post(request,send);
    connect(m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), m_reply, SLOT(ignoreSslErrors()));
    connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
    connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SIGNAL(error(QNetworkReply::NetworkError)));
    connect(m_reply, SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(uploadProgress(qint64,qint64)));

    return m_reply;
}

void CReplicator::readData(QNetworkReply *reply)
{
    m_replyCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
//    debug("Reply code: " + QString::number(m_replyCode));

    m_replyPhrase = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
//    debug("Reply phrase: " + m_replyPhrase);

    m_replyData = reply->readAll();
    debug("Reply: " + QString::fromUtf8(m_replyData));

    if(reply->error())
    {
        debug("Error code: " + QString::number(reply->error()) + ". Error string: " + reply->errorString());
        reply->abort();
    }

    delete reply;

    emit received(m_replyCode);
}

void CReplicator::sslerr(QNetworkReply* reply, const QList<QSslError>&)
{
    debug("SSL Error");
    reply->ignoreSslErrors();
}

void CReplicator::synchronize()
{
    addField("action", "sync");

    QList<CChange> changes = m_db->changes();
    QString path = "changes[%1][%2]";
    // сделано убого. лучше изначально генерить QMap
    foreach(CChange c, changes)
    {
/*        QString id = map->value("entry_id");
        foreach(QString key, map->keys())
            addField(path.arg(id, key), map->value(key).toString());*/

        addField(path.arg(c.id, "id"), c.id);
        debug("Added change with id: " + c.id);
        addField(path.arg(c.id, "type"), QString::number(c.type));
        addField(path.arg(c.id, "action"), QString::number(c.action));
        addField(path.arg(c.id, "datetime"), c.datetime);
    }

    QList<ChangeMap*> list = m_db->changedEntries();
    path = "entries[%1][%2]";
    foreach(ChangeMap* map, list)
    {
        QString id = map->value("id").toString();
        foreach(QString key, map->keys())
            addField(path.arg(id, key), map->value(key).toString());
    }
    qDeleteAll(list);
    list.clear();

    list = m_db->changedFields();
    path = "fields[%1][%2]";
    foreach(ChangeMap* map, list)
    {
        QString id = map->value("id").toString();
        foreach(QString key, map->keys())
            addField(path.arg(id, key), map->value(key).toString());
    }
    qDeleteAll(list);
    list.clear();

    list = m_db->changedSections();
    path = "sections[%1][%2]";
    foreach(ChangeMap* map, list)
    {
        QString id = map->value("id").toString();
        foreach(QString key, map->keys())
            addField(path.arg(id, key), map->value(key).toString());
    }
    qDeleteAll(list);
    list.clear();

    QStringList str_list;
    str_list << "cipher" << "md5";

    QMap<QString, QString> map = m_db->properties(str_list);
    foreach(QString key, map.keys())
        addField(QString("properties[%1]").arg(key), map.value(key));

    addField("last_sync", m_db->lastSync());
    debug("Last sync: " + m_db->lastSync());

    post();
}
